<?php

namespace App\Handlers\Commands;

use App\Commands\AddRemoveVulnerabilityToFromFolder as AddRemoveVulnerabilityToFromFolderCommand;
use App\Entities\Folder;
use App\Entities\Vulnerability;
use App\Exceptions\ActionNotPermittedException;
use App\Exceptions\FileNotFoundException;
use App\Exceptions\FolderNotFoundException;
use App\Exceptions\InvalidInputException;
use App\Exceptions\VulnerabilityNotFoundException;
use App\Policies\ComponentPolicy;
use App\Repositories\FileRepository;
use App\Repositories\FolderRepository;
use App\Repositories\VulnerabilityRepository;
use Doctrine\ORM\EntityManager;

class AddRemoveVulnerabilityToFromFolder extends CommandHandler
{
    /** @var FolderRepository */
    protected $folderRepository;

    /** @var VulnerabilityRepository */
    protected $vulnerabilityRepository;

    /** @var FileRepository */
    protected $fileRepository;

    /** @var EntityManager */
    protected $em;

    /**
     * AddVulnerabilityToFolder constructor.
     *
     * @param FolderRepository $folderRepository
     * @param VulnerabilityRepository $vulnerabilityRepository
     * @param FileRepository $fileRepository
     * @param EntityManager $em
     */
    public function __construct(
        FolderRepository $folderRepository, VulnerabilityRepository $vulnerabilityRepository,
        FileRepository $fileRepository, EntityManager $em
    )
    {
        $this->folderRepository                 = $folderRepository;
        $this->vulnerabilityRepository          = $vulnerabilityRepository;
        $this->fileRepository                   = $fileRepository;
        $this->em                               = $em;
    }

    /**
     * Process the AddVulnerabilityToFolderCommand.
     *
     * @param AddRemoveVulnerabilityToFromFolderCommand $command
     * @return Folder
     * @throws ActionNotPermittedException
     * @throws FileNotFoundException
     * @throws FolderNotFoundException
     * @throws InvalidInputException
     * @throws VulnerabilityNotFoundException
     */
    public function handle(AddRemoveVulnerabilityToFromFolderCommand $command)
    {
        $requestingUser = $this->authenticate();

        $folderId        = $command->getFolderId();
        $vulnerabilityId = $command->getVulnerabilityId();
        if (!isset($folderId, $vulnerabilityId)) {
            throw new InvalidInputException("One or more required members are not set on the command");
        }

        /** @var Folder $folder */
        $folder = $this->folderRepository->find($folderId);
        if (empty($folder)) {
            throw new FolderNotFoundException("A Folder with the given ID does not exist");
        }

        /** @var Vulnerability $vulnerability */
        $vulnerability = $this->vulnerabilityRepository->find($vulnerabilityId);
        if (empty($vulnerability)) {
            throw new VulnerabilityNotFoundException("A Vulnerability with the given ID does not exist");
        }

        // Check that the requesting User has permission to add a Vulnerability to this Folder
        if ($requestingUser->cannot(ComponentPolicy::ACTION_EDIT, $folder)) {
            throw new ActionNotPermittedException(
                "The requesting User does not have permission to add Vulnerabilities to the given Folder"
            );
        }

        if ($command->isRemove()) {
            $folder->removeVulnerability($vulnerability);
        } else {
            $folder->addVulnerability($vulnerability);
        }

        $this->em->persist($folder);
        $this->em->flush($folder);

        return $folder;
    }
}